package server

import (
	"context"
	"fmt"
	"log"
	"math/big"

	cp_zkp "github.com/linchao/zkp_auth/internal/cpzkp"
	"github.com/linchao/zkp_auth/models"
)

// VerifyZKPProofImpl 验证客户端提供的Chaum-Pedersen零知识证明
//
// 该函数:
// 1. 从数据库查询设备对应的公钥参数(y1, y2)
// 2. 使用Chaum-Pedersen协议验证零知识证明
// 3. 返回验证结果(成功或失败)
//
// 参数:
//   - deviceID: 设备唯一标识符
//   - nonce: 用于防止重放攻击的随机数
//   - r1: ZKP承诺值r1
//   - r2: ZKP承诺值r2
//   - c: ZKP挑战值
//   - s: ZKP响应值
//
// 返回:
//   - 如果验证成功则返回true，否则返回false
//   - 如果验证过程中发生错误，返回error
func VerifyZKPProofImpl(deviceID, nonce, r1, r2, c, s string) (bool, error) {
	// 1. 记录验证操作开始
	log.Printf("正在验证设备 %s 的零知识证明", deviceID)

	// 2. 从数据库获取设备公钥参数
	device, err := getDeviceFromDatabase(deviceID)
	if err != nil {
		return false, fmt.Errorf("获取设备信息失败: %v", err)
	}

	// 3. 检查设备是否存在
	if device == nil {
		return false, fmt.Errorf("设备 %s 未注册", deviceID)
	}

	// 4. 解析字符串为大整数
	r1Int, ok := new(big.Int).SetString(r1, 10)
	if !ok {
		return false, fmt.Errorf("解析r1值失败")
	}

	r2Int, ok := new(big.Int).SetString(r2, 10)
	if !ok {
		return false, fmt.Errorf("解析r2值失败")
	}

	cInt, ok := new(big.Int).SetString(c, 10)
	if !ok {
		return false, fmt.Errorf("解析c值失败")
	}

	sInt, ok := new(big.Int).SetString(s, 10)
	if !ok {
		return false, fmt.Errorf("解析s值失败")
	}

	y1Int, ok := new(big.Int).SetString(device.Y1, 10)
	if !ok {
		return false, fmt.Errorf("解析设备公钥y1失败")
	}

	y2Int, ok := new(big.Int).SetString(device.Y2, 10)
	if !ok {
		return false, fmt.Errorf("解析设备公钥y2失败")
	}

	// 5. 初始化CPZKP系统参数
	cpzkp, err := cp_zkp.NewCPZKP()
	if err != nil {
		return false, fmt.Errorf("初始化CPZKP失败: %v", err)
	}

	params, err := cpzkp.InitCPZKPParams()
	if err != nil {
		return false, fmt.Errorf("初始化ZKP参数失败: %v", err)
	}

	// 6. 创建验证器并验证证明
	verifier := &cp_zkp.Verifier{}
	isValid := verifier.VerifyProof(y1Int, y2Int, r1Int, r2Int, cInt, sInt, params)

	// 7. 记录验证结果
	if isValid {
		log.Printf("设备 %s 的零知识证明验证成功", deviceID)
	} else {
		log.Printf("设备 %s 的零知识证明验证失败", deviceID)
	}

	return isValid, nil
}

// getDeviceFromDatabase 从数据库获取设备信息
//
// 该函数通过设备ID查询数据库，获取相应的公钥参数(y1, y2)
//
// 参数:
//   - deviceID: 设备唯一标识符
//
// 返回:
//   - 返回包含设备公钥信息的结构体指针，若设备不存在则返回nil
//   - 如果查询过程中发生错误，返回error
func getDeviceFromDatabase(deviceID string) (*models.Device, error) {
	// 创建上下文
	ctx := context.Background()

	// 从数据库查询设备信息
	device, err := models.GetDevice(ctx, deviceID)
	if err != nil {
		return nil, fmt.Errorf("数据库查询错误: %v", err)
	}

	// 设备不存在的情况
	if device == nil {
		log.Printf("设备 %s 在数据库中不存在", deviceID)
		return nil, nil
	}

	return device, nil
}
